package com.tuomaxu.mvcframework.core;

import com.tuomaxu.mvcframework.annotation.*;
import com.tuomaxu.mvcframework.pojo.XYHandle;
import com.tuomaxu.mvcframework.utils.ScannerPackageUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

public class XYDispatcherServlet extends HttpServlet {

    //缓存配置信息
    private Properties properties = new Properties();

    //缓存需要动态创建实例的类的全限定类名
    private List<String> classNameList = new ArrayList<>();

    //ioc容器
    private Map<String,Object> ioc = new HashMap<>();

    //handleMap
    private Map<String, XYHandle> handleMap = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        //super.init(config);
        //1，加载配置文件
        doLoadConfig(config);

        //2，扫描包下被标记的类
        doScan();

        //3，创建对象
        doInstance();

        for (String key : ioc.keySet()){
            Object obj = ioc.get(key);
            //4, 注入依赖
            doAutowried(obj);
            //5，处理handleMapping和Security
            doHandlMapping(obj);
        }
        System.out.println("初始化完成");
    }

    private void doLoadConfig(ServletConfig config) {
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void doScan() {
        String scanPackageName = properties.getProperty("scanPackage");
        classNameList = ScannerPackageUtils.scanner(scanPackageName);
    }

    private void doInstance() {
        try {
            for (String className : classNameList) {
                Class<?> aClass = Class.forName(className);
                if(aClass.isAnnotationPresent(XYController.class)){
                    Object o = aClass.newInstance();
                    String value = aClass.getAnnotation(XYController.class).value();
                    String beanId = aClass.getName();
                    if(!value.equals("")){
                        beanId = value;
                    }
                    ioc.put(beanId,o);
                }
                if(aClass.isAnnotationPresent(XYService.class)){
                    Object o = aClass.newInstance();
                    String value = aClass.getAnnotation(XYService.class).value();
                    String beanId = aClass.getName();
                    if(!value.equals("")){
                        beanId = value;
                    }
                    ioc.put(beanId,o);
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void doAutowried(Object obj) {
        try {
            Class<?> aClass = obj.getClass();
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if(field.isAnnotationPresent(XYAutowired.class)){
                    //获取字段类型名
                    String beanId = field.getType().getName();
                    String value = field.getAnnotation(XYAutowired.class).value();
                    if(!value.equals("")){
                        beanId = value;
                    }
                    Object o = ioc.get(beanId);
                    field.setAccessible(true);
                    field.set(obj,o);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private void doHandlMapping(Object obj) {
        try {
            Class<?> aClass = obj.getClass();
            String base = "";
            if(aClass.isAnnotationPresent(XYRequestMapping.class)){
                base = aClass.getAnnotation(XYRequestMapping.class).value();
            }
            //考虑Class上的权限是(a,b)
            //方法t1的权限是c，此时t1方法a,b,c都可以方法
            //class与metho上的权限集合是求并
            //初始化一个空的classSecuritys数组，
            //处理class权限
            String[] classSecuritys = {};
            if(aClass.isAnnotationPresent(XYSecurity.class)){
                classSecuritys = aClass.getAnnotation(XYSecurity.class).value();
            }

            //获取public方法
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if(method.isAnnotationPresent(XYRequestMapping.class)){
                    String value = method.getAnnotation(XYRequestMapping.class).value();
                    String url = base + value;
                    XYHandle xyHandle = new XYHandle();
                    xyHandle.setUrl(url);
                    xyHandle.setMethod(method);
                    xyHandle.setObject(obj);
                    //处理参数
                    Parameter[] parameters = method.getParameters();
                    for (Parameter parameter : parameters) {
                        String name = parameter.getType().getName();
                        xyHandle.getParams().add(name);
                    }

                    //处理方法权限
                    String[] methodSecuritys = {};
                    if(method.isAnnotationPresent(XYSecurity.class)){
                        methodSecuritys = method.getAnnotation(XYSecurity.class).value();
                    }

                    xyHandle.getSecuritys().addAll(Arrays.asList(classSecuritys.clone()));
                    xyHandle.getSecuritys().addAll(Arrays.asList(methodSecuritys.clone()));

                    handleMap.put(url,xyHandle);
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }


    }



    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doGet(req, resp);
//        System.out.println("ok");
//        resp.getWriter().write("ok");

        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doPost(req, resp);
//        resp.getWriter().write("ok");
        /*
        1，通过请求数据获取如下数据
            参数username
            请求路径

        2，通过请求路径找到对应的handle对象

        3，判断是否有权限，
            如果有调用实际的handle处理方法并传递参数，返回ok+username
            如果没有，直接返回403+username
         */

        if(handleMap.size() == 0){
            resp.getWriter().write("500:no handler init");
            return;
        }

        //这里假定参数只有一个username
        String username = req.getParameter("username");
        String url = req.getRequestURI();

        XYHandle xyHandle = handleMap.get(url);
        if(xyHandle == null){
            resp.getWriter().write("404:no handler process");
            return;
        }

        //检查权限，如果没有权限，拦截
        if(!xyHandle.getSecuritys().contains(username)){
            resp.getWriter().write("403: forbidden");
            return;
        }

        Map<String,Object> paramsMap = new HashMap<>();
        paramsMap.put(req.getClass().getName(),req);
        paramsMap.put(resp.getClass().getName(),resp);
        paramsMap.put(username.getClass().getName(),username);

        Method method = xyHandle.getMethod();
        Object object = xyHandle.getObject();

        List<String> paramTypes = xyHandle.getParams();

        List<Object>args = new ArrayList<>();

        for (String paramType : paramTypes) {
            Object o = paramsMap.get(paramType);
            args.add(o);
        }

        try {
           String result = (String) method.invoke(object,args.toArray());
            resp.getWriter().write(result);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}
